/******************************************************************************
*
* This file is part of FreeTrack (http://code.google.com/p/qtfreetrack).
* BY Willy Gardiol (willy@gardiol.org)
*
* FreeTrack is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* FreeTrack is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Foobar.  If not, see <http://www.gnu.org/licenses/>
*
******************************************************************************/
#include "trackpoint.h"

TrackPoint::TrackPoint( QGeoCoordinate &coord, QDateTime time, qreal p, qreal s )
{
    altitude = coord.altitude();
    position = QPointF( coord.longitude(), coord.latitude() );
    speed = s;
    timestamp = time;
    name = description = "";
    setPrecision( p );
}

TrackPoint::TrackPoint(const TrackPoint& other )
{
    timestamp = other.timestamp;
    position = other.position;
    precision = other.precision;
    precision_ll = other.precision_ll;
    altitude = other.altitude;
    speed = other.speed;
    name = other.name;
    description = other.description;
}

TrackPoint::TrackPoint(const QPointF& pt ) :
        timestamp(),
        position(pt),
        altitude(0.0),
        speed(0.0),
        name(""),
        description("")
{
    setPrecision(1.0);
}

TrackPoint::TrackPoint() :
        timestamp(),
        position(),
        altitude(0.0),
        speed(0.0),
        name(""),
        description("")
{
        setPrecision(1.0);
}

TrackPoint::TrackPoint(QXmlStreamReader* stream) :
        timestamp(),
        position(),
        precision(1.0),
        altitude(0.0),
        speed(0.0),
        name(""),
        description("")
{
    fromGpx( stream );
}

void TrackPoint::fromGpx(QXmlStreamReader *stream)
{
    QXmlStreamAttributes attributes = stream->attributes();
    QString lat = attributes.value("lat").toString();
    QString lon = attributes.value("lon").toString();
    lat.truncate(10);
    lon.truncate(10);
    position = QPointF( lon.toDouble(), lat.toDouble() );

    QString tmp;

    stream->readNext();
    while ( !(stream->isEndElement() &&
            stream->name() == "trkpt") )
    {
        if ( stream->isStartElement() )
        {
            if ( stream->name() == "ele")
            {
                 stream->readNext();
                 tmp = stream->text().toString();
                 tmp.truncate(10);
                 altitude = tmp.toDouble();
            }
            else if ( stream->name() == "time")
            {
                 stream->readNext();
                 timestamp = QDateTime::fromString( stream->text().toString(),"yyyy'-'MM'-'dd'T'hh':'mm':'ss'Z'" );
            }
            else if ( stream->name() == "speed")
            {
                 stream->readNext();
                 tmp = stream->text().toString();
                 tmp.truncate(10);
                 speed = tmp.toDouble();
            }
            else if ( stream->name() == "hdop")
            {
                 stream->readNext();
                 tmp = stream->text().toString();
                 tmp.truncate(10);
                 precision = tmp.toDouble();
                 if ( precision < 1.0 )
                     precision = 1.0;
            }
            else if ( stream->name() == "name")
            {
                 stream->readNext();
                 name = stream->text().toString();
            }
            else if ( stream->name() == "desc")
            {
                 stream->readNext();
                 description = stream->text().toString();
            }
        }
        stream->readNext();
    }
}

QString TrackPoint::toTxt() const
{
    QPointF point = position;
    QString lat = QString("%1").arg(point.y(), 0, 'f', 7);
    QString lon = QString("%1").arg(point.x(),0,'f',7);
    QString alt = QString("%1").arg(altitude,0,'f',7);
    QString pre = QString("%1").arg(precision, 0, 'f', 2);
    QString spd = QString("%1").arg(speed, 0, 'f', 2);

    QString fixed_name = name;
    QString fixed_desc = description;
    fixed_name.replace("'", " " );
    fixed_desc.replace("'", " " );
    return QString("%1 %2 %3 %4 %5 %6 '%7'%8'").arg(lat)
                                       .arg(lon)
                                       .arg(alt)
                                       .arg(spd)
                                       .arg(pre)
                                       .arg(timestamp.toString("yyyy-MM-ddThh:mm:ssZ"))
                                       .arg(fixed_name)
                                       .arg(fixed_desc);
}

bool TrackPoint::fromTxt(const QString& row )
{

    QRegExp rx("\\s*"REGEXP_NUMBER" "REGEXP_NUMBER" "REGEXP_NUMBER" "REGEXP_NUMBER" "REGEXP_NUMBER" "REGEXP_DATE" '(.*)'(.*)'");

    if ( rx.indexIn(row) > -1 )
    {
        position.setY( rx.cap(1).toDouble() );
        position.setX( rx.cap(2).toDouble() );
        altitude = rx.cap(3).toDouble();
        speed = rx.cap(4).toDouble();
        precision = rx.cap(5).toDouble();
        timestamp = QDateTime::fromString( rx.cap(6),"yyyy'-'MM'-'dd'T'hh':'mm':'ss'Z'" );
        name = rx.cap(7);
        description = rx.cap(8);
        return true;
    }
    return false;
}

TrackPoint::~TrackPoint()
{
}

qreal TrackPoint::getSpeed() const
{
    return speed;
}

TrackPoint::operator QPointF() const
{
    return (QPointF)position;
}

bool TrackPoint::isOverlapping(const TrackPoint& pt) const
{
    qreal distance = distanceFrom( pt.position );
    return distance <= (precision+pt.precision);
}

void TrackPoint::toGpx(QXmlStreamWriter* stream) const
{
    QPointF point = position;
    QString lat = QString("%1").arg(point.y(), 0, 'f', 7);
    QString lon = QString("%1").arg(point.x(),0,'f',7);
    QString alt = QString("%1").arg(altitude,0,'f',7);
    QString pre = QString("%1").arg(precision, 0, 'f', 2);
    QString spd = QString("%1").arg(speed, 0, 'f', 2);

    stream->writeStartElement("trkpt");
    stream->writeAttribute("lat", lat);
    stream->writeAttribute("lon", lon);
    stream->writeTextElement("ele", alt);
    stream->writeTextElement("speed", spd);
    stream->writeTextElement("time", timestamp.toString("yyyy-MM-ddThh:mm:ssZ"));
    stream->writeTextElement("hdop", pre);
    if ( name != "" )
        stream->writeTextElement("name", name);
    if ( description != "" )
        stream->writeTextElement("desc", description);

    stream->writeEndElement();
}

qreal TrackPoint::getPrecision() const
{
    return precision;
}

void TrackPoint::setPrecision(qreal p)
{
    precision = p;
    precision_ll = convertM2LL(precision);
}

QDateTime TrackPoint::getTimestamp() const
{
    return timestamp;
}

QPoint TrackPoint::draw(QPainter & painter, QColor color, const QTransform& matrix) const
{
    // Convert point to screen cohordinates
    QPoint screenPt = (position * matrix).toPoint();
    // Convert lat/lon precision to screen precision
    int screenPrec = matrix.m11() * precision_ll;

    painter.save();
    painter.setPen(QPen( QBrush(color), 0 ) );
    // Draw the ellipse only if its big enough to be drawn
    if ( screenPrec > 1 )
        painter.drawEllipse( screenPt, screenPrec, screenPrec );
    painter.drawPoint( screenPt );
    painter.restore();

    return screenPt;
}

qreal TrackPoint::distanceFrom(const QGeoCoordinate & coord ) const
{
    return coord.distanceTo( QGeoCoordinate( position.y(), position.x() ) );
}

qreal TrackPoint::distanceFrom(const QPointF & point ) const
{
    return distanceFrom( QGeoCoordinate( point.y(), point.x() ) );
}

qreal TrackPoint::distanceFrom(const TrackPoint& tp ) const
{
    return distanceFrom( QGeoCoordinate( tp.position.y(), tp.position.x() ) );
}

qreal TrackPoint::convertM2LL(qreal m) const
{
 /*   qreal deg2rad = 3.1415/180.0;
    qreal R = 6371000.0; // m

    qreal cosLat2 = cos( position.y()*position.y()*deg2rad*deg2rad );
  //  return acos( cos(m) / (R*cosLat2) +1.0 - 1.0 / cosLat2 );


    ar lat2 = Math.asin( Math.sin(lat1)*Math.cos(d/R) +
                          Math.cos(lat1)*Math.sin(d/R)*Math.cos(brng) );

    var lon2 = lon1 + Math.atan2(Math.sin(brng)*Math.sin(d/R)*Math.cos(lat1),
                                 Math.cos(d/R)-Math.sin(lat1)*Math.sin(lat2));

    qreal lat1 = position.y()*deg2rad;
    qreal lat2 = asin( sin(lat1)*cos(m/R) + cos(lat1) * sin(m/R) * cos(0.0) );

    qreal lon2 = atan2( 0.0, cos( m/R ) - sin(lat1)*sin(lat2) );
    return lon2;*/
    return 0.00001*m;
}

qreal TrackPoint::convertLL2M(qreal lon) const
{
    return lon/0.00001;
}
